/*
 * Copyright (C) 2019-2020 Alibaba Group Holding Limited
 */

#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <string.h>
#include <unistd.h>

#include <aos/ringbuffer.h>
#include <coapi_comm.h>
#include <coapi_common.h>
#include <coapi_bindcode.h>
#include <coapi_devbind.h>
#include <coapi_ota.h>
#include <coapi_timer.h>
#include <coapi_audio.h>
#include <coapi_logic.h>
#include <coapi_record.h>
#include <coapi_register.h>
#include <coapi_speech.h>
#include <coapi_stream.h>

#include "app_main.h"

#define TAG "auicoapi"

#define COAPI_CTX_MESSAGE_NUM (20)
#define COAPI_CTX_FRAME_SIZE  (1280)

typedef struct {
    unsigned char boot_completed;  /* 设备boot完成标记 */
    unsigned char g_coapp_init_ok; /* coapp 初始化完成标记，注册成功 */
    char *        recv_buf;        /* 接收缓冲区 */
    dev_ringbuf_t ring_buffer;     /* 环形缓冲区 */
    int8_t        stop_flag;       /* 为1，需要执行停止喂数据；为0，执行停止喂数据完成 */
    int8_t        feed_flag;       /* 为1，可喂数据；为0不可喂数据 */
} coapi_ctx_t;
static coapi_ctx_t coapi_ctx = {0};

/**
 * 注意：此处的appkey和secrectkey 仅供平头哥测试使用，不可外发其他客户商用（有激活ID限制）
 */
#define T_HEAD_APP_KEY     "21600133650195"
#define T_HEAD_SECRECT_KEY "1a294db345be6e98324eab06faa98349"

/****************************************/
/*          internal APIs               */
/****************************************/
/**
 * @breif 与云端的连接回调
 * 连接上云端后所有coapi的功能才能使用
 * 如需判断是否已经连接上云端使用coapi_logic.h的coapi_connect_status()
 * 函数主动查询,比回调实时性更好
 */
static void coapp_connect_cb(unsigned char status)
{
    switch (status) {
    case 0:
        LOGD(TAG, "未连接 ");
        break;
    case 1:
        LOGD(TAG, "连接中 ");
        break;
    case 2:
        LOGD(TAG, "已连接 ");
        break;
    case 3:
        LOGD(TAG, "链接关闭 ");
        break;
    case 4:
        LOGD(TAG, "网络差，信号弱 ");
        /* 播放网络不稳定提示音 */
        // local_audio_play(LOCAL_AUDIO_NET_UNSTABLE);
        break;
    case 5:
        LOGD(TAG, "重新连接");
        break;

    case 6:
    default:
        LOGD(TAG, "未知状态 ");
        break;
    }
}

/*
 * @brief 获取网络状态，由开发者实现函数体逻辑
 * @return 0: 网络断开
 *         1: 网络连接
 * 注意事项: 函数体的运行逻辑尽量简单，不能有耗时的操作，运行耗时<10毫秒
 */
static int coapp_wifi_cb(void)
{
    /*
	 * #define NTE_STATE_CONNECTING 	0
	 * #define NTE_STATE_OK 			1
	 * #define NTE_STATE_ERR 			2
	 */
    int res = 0;

    res = wifi_internet_is_connected();

    return res;
}
/*
 * @brief 设备绑定回调
 * @return  无
 */
static void dev_bind_cb(int result, char *str)
{
    LOGD(TAG, "bind result: %d ", result);
}
/*
 * @brief 设备绑定码回调
 * @return  无
 */
static void dev_bindcode_cb(int status, char *url, char *code)
{
    LOGD(TAG, "bindcode: %s", code);

    coapi_player_start(url, NULL);
}
/*
 * @brief 设备绑定微信回调
 * @return  无
 */
static void dev_wxbind_cb(int result)
{
    LOGD(TAG, "==============wxbind result: %d=============\n", result);
}

/*
 * @brief 设备注册回调
 * @return  无
 */
static void dev_register_cb(unsigned char result, char *devid, char *uuid, char *wxid)
{
    int rec = 0;

    LOGD(TAG, "result: %d\n", result);

    if (result != AUTH_STATE_AUTH_OK && result != AUTH_STATE_HAS_AUTH) {
        LOGE(TAG, "coapi register fail !\n");
        return;
    }
    LOGD(TAG, "coapi registe success");

    if (wxid) {
        LOGD(TAG, "=======  wxid: %s ========", wxid);
    }

    if (0 /* airkiss network cfg */) {
        /* 针对airkiss配网，通过设备端播放绑定码绑定,非airkiss配网可删除,此行代码 */
        coapi_bindcode_start(dev_bindcode_cb);
    } else {
        int rec = 0;
        rec     = coapi_devbind_start(dev_bind_cb, 0);
    }

    /* 从云端更新最近的TIMER_LOCAL_CNT个闹钟到本地 */
    rec = coapi_timer_req(TIMER_LOCAL_CNT);
    if (0 != rec) {
        LOGD(TAG, "flash local timer cnt fail");
    }
    /* 每日推荐开机播放 */
    if (coapi_ctx.boot_completed == 0) {
        coapi_daily_play();
    }
    /* 标记coapi 初始化完成 */
    coapi_ctx.g_coapp_init_ok = 1;
    return;
}

static int coapi_get_wifi_mac(char *mac_str)
{
    int     i      = 0;
    uint8_t mac[6] = {0};

    if (NULL == mac_str) {
        return -1;
    }

    wifi_getmac(mac);

    for (i = 0; i < 6; i++) {
        sprintf(mac_str + 2 * i, "%02X", mac[i]);
    }
    mac_str[12] = '\0';

    return 0;
}

/**
 * 传入设备唯一id,如: MAC/SN/IMEI/..., 可选择云端分配的方式传入NULL,
 * 参考coapi_register.h
 * ssid: wifi名称用于公众号显示
 */
static void coapi_init(char *device_id, char *ssid)
{
    /* 根据产品实际情况修改 */
    dev_comm dev = {
        .devtype = "机器人",
        .ver     = "dg_0.01",
        .cpu     = "cb5654",
        .ops     = "aos",
        .name    = "小酷",
        .vendor  = "酷旗",
    };

    strcpy(dev.devid, device_id);
    /*
     * coapi_dev_init()接口可以在任何地方调用，
     * 但必须在coapi_register_start被调用之前被调用
     */
    coapi_dev_init(&dev);
    /*
     *  coapi_logic_init(void)接口建议在设备主程序初
     *  始化时调用(注意：必须确保在进行声波配网之前调用).
     *
     *  默认是COAPI_SERVER_GRAY即灰度服务器, 切记生产时切换到生产服务器 COAPI_SERVER_SVR
     */
    coapi_logic_init(coapp_connect_cb, coapp_wifi_cb, COAPI_SERVER_SVR);
    /*
     *  coapi_comm_init(void)接口建议在设备主程序初
     *  始化时调用(注意：尽可能在进行声波配网之前调用)
     */
    coapi_comm_init();
    /*
     * coapi_register_start()接口可以在任何地方调用，但是在调用之前必须确保
     * coapi_logic_init(void)、coapi_comm_init(void)、coapi_dev_init(dev_comm *dev)
	 * 已经初始化完毕
	 * appkey "21565831671647" 测试用，实际使用需联系酷旗（cocheer.net） 获取
	 * secretkey "3ed3c444f0b197d5b9206ec89045dc42" 测试用，实际使用需联系酷旗（cocheer.net） 获取
     */
    coapi_register_start(dev_register_cb, T_HEAD_APP_KEY, T_HEAD_SECRECT_KEY, ssid, NULL, 0);

    /* coapi_ctx 初始化 */
    coapi_ctx.recv_buf = aos_malloc_check(COAPI_CTX_FRAME_SIZE * COAPI_CTX_MESSAGE_NUM);
    if (NULL == coapi_ctx.recv_buf) {
        LOGE(TAG, "recv buf malloc failed!");
        return;
    }
    ringbuffer_create(&coapi_ctx.ring_buffer, coapi_ctx.recv_buf,
                      COAPI_CTX_FRAME_SIZE * COAPI_CTX_MESSAGE_NUM);

    return;
}

static void coapi_main_task(void *arg)
{
    int     ret          = 0;
    char    wifi_mac[14] = {};
    uint8_t rbuf[COAPI_CTX_FRAME_SIZE];

    while (!wifi_internet_is_connected()) {
        aos_msleep(500);
    };
    aos_msleep(500);

    /* 获取mac地址 */
    ret = coapi_get_wifi_mac(wifi_mac);
    /* CoAPI初始化 */
    if (0 == ret) {
        LOGD(TAG, " wifi mac: %s", wifi_mac);
        coapi_init(wifi_mac, NULL);
    } else {
        coapi_init(NULL, NULL);
    }

    while (1) {
        if (!coapi_ctx.g_coapp_init_ok) {
            goto coapi_main_task_continue;
        }
        if (1 == coapi_ctx.stop_flag) {
            int32_t len = 0;

            /* 获取当前ring_buffer数据长度 */
            len = ringbuffer_available_read_space(&coapi_ctx.ring_buffer);
            while (len >= 0) {
                if (len <= COAPI_CTX_FRAME_SIZE) {
                    memset(rbuf, 0, sizeof(rbuf));
                    ringbuffer_read(&coapi_ctx.ring_buffer, rbuf, len);
                    ret = coapi_stream_feed((char *)rbuf, COAPI_CTX_FRAME_SIZE, RECORD_END);
                    LOGE(TAG, "coapi_stream_feed last ok!");
                } else {
                    ringbuffer_read(&coapi_ctx.ring_buffer, rbuf, COAPI_CTX_FRAME_SIZE);
                    ret = coapi_stream_feed((char *)rbuf, COAPI_CTX_FRAME_SIZE, RECORD_CONTINUES);
                }
                if (ret < 0) {
                    LOGE(TAG, "coapi_stream_feed failed!");
                }
                len -= COAPI_CTX_FRAME_SIZE;
                aos_msleep(10);
            }
            ringbuffer_clear(&coapi_ctx.ring_buffer);
            coapi_speech_stop();
            coapi_ctx.stop_flag = 0;
        } else {
            if (ringbuffer_available_read_space(&coapi_ctx.ring_buffer) < COAPI_CTX_FRAME_SIZE) {
                goto coapi_main_task_continue;
            }
            ret = ringbuffer_read(&coapi_ctx.ring_buffer, rbuf, COAPI_CTX_FRAME_SIZE);
            if (ret != COAPI_CTX_FRAME_SIZE) {
                LOGE(TAG, "read size[%d] incorrect!", ret);
                goto coapi_main_task_continue;
            }
            if (coapi_ctx.feed_flag == 1) {
                ret = coapi_stream_feed((char *)rbuf, COAPI_CTX_FRAME_SIZE, RECORD_CONTINUES);
                if (ret < 0) {
                    LOGE(TAG, "coapi_stream_feed failed!");
                }
            }
        }
    coapi_main_task_continue:
        aos_msleep(25);
    }
}

/****************************************/
/*          external APIs               */
/****************************************/
/*
 * @brief 上传音频，由于MinD算法返回数据大小和酷旗要求不一致，需要转换
 * @in	   data: 数据内容
 * 		   size: 数据大小
 * @return 推送状态，0：成功
 * 					<0：失败
 */
int app_aui_cloud_push_audio(void *data, size_t size)
{
    int ret = 0;
    if (coapi_ctx.g_coapp_init_ok) {
        ret = ringbuffer_write(&coapi_ctx.ring_buffer, data, size);
        if (ret < size) {
            LOGE(TAG, "buf full, len(%d)<size(%d)", ret, size);
        }
    } else {
        LOGE(TAG, "coapi is not inited!");
        ret = -1;
    }
    return ret;
}

/*
 * @brief 通知云端开始一次对话
 * @in	   do_wwv: 是否启用云端二次确认
 * @return 0：成功
 * 		   <0：失败
 */
int app_aui_cloud_start(int do_wwv)
{
    if (coapi_ctx.g_coapp_init_ok == 1) {
        /* 如果上一轮对话还在进行，取消 */
        if (FEED_STOP != coapi_stream_status()) {
            LOGW(TAG, "feed is still running, cancel first!");
            coapi_stream_cancel();
        }
        aos_msleep(2);
        LOGW(TAG, "app_aui_cloud_start!");
        /* 开启新一轮对话 */
        coapi_speech_start();
        /* 清除环形缓冲区 */
        ringbuffer_clear(&coapi_ctx.ring_buffer);
        /* 开放数据喂入 */
        coapi_ctx.feed_flag = 1;
    } else {
        LOGE(TAG, "coapi is not inited!");
        return -1;
    }
    return 0;
}

/* 创建新线程 */
int app_aui_nlp_init(void)
{
    aos_task_t task_handle;
    aos_task_new_ext(&task_handle, "coapi_init", coapi_main_task, NULL, 4 * 1024, 29);

    return 0;
}

/*
 * @brief 通知云端当前对话结束
 * @in	   force_stop: 是否强制停止
 * @return 0：成功
 * 		   <0：失败
 */
int app_aui_cloud_stop(int force_stop)
{
    if (coapi_ctx.g_coapp_init_ok == 1) {
        LOGD(TAG, "app_aui_cloud_stop!");

        if (coapi_ctx.feed_flag == 1) {
            /* 标记执行停止喂数据后的数据尾帧喂入 */
            coapi_ctx.stop_flag = 1;
        }
        /* 关闭数据喂入 */
        coapi_ctx.feed_flag = 0;
    }
    return 0;
}

/*
 * 云端二次确认相关API，由于暂未实现云端二次确认功能，先预留接口
 */
int app_aui_get_wwv_result(unsigned int timeout)
{
    return 0;
}

int app_aui_push_wwv_data(void *data, size_t len)
{
    return 0;
}

int app_aui_cloud_push_text(char *text)
{
    return 0;
}

int app_aui_cloud_tts_run(const char *text, int wait_last)
{
    return 0;
}

int app_aui_cloud_start_tts()
{
    return 0;
}

int app_aui_cloud_stop_tts()
{
    return 0;
}

void app_tts_update_running(tts_state_t state)
{
    return;
}

/*
 * @brief 用于判断机器是否开机完成，用于启动流程初始化的接口调用判断选择
 * @return 0: 未启动完成
 *         1: 启动完成
 */
int coapi_boot_completed(void)
{
    coapi_ctx.boot_completed = 1;

    return 1;
}

int coapi_deinit(void)
{
    int rec = 0;
    rec     = coapi_comm_deinit();

    coapi_logic_deinit();

    coapi_dev_deinit();
    return 0;
}
